package edu.uwm.RobberKnight.Model;

import java.awt.Color;
import java.util.AbstractMap.SimpleEntry;
import java.util.LinkedList;

public class RobberKnightsGame  extends Logger implements java.io.Serializable {


	private static final long serialVersionUID = 6243328038114409292L;
	private Board _board;
	//Store the players in order of turn, remove the players that are finished playing.
    private LinkedList<Player> _playerTurnOrder;
    //Keep track of all players regardless of state or turn orer.
    private LinkedList<Player> _players;
    private boolean _started;
    private GameState _state;
    private Tile _selectedTile;
    
    public RobberKnightsGame() {
        _state = GameState.initialTilePlacement;
        _started = false;
        _playerTurnOrder = new LinkedList<Player>();
        _players = new LinkedList<Player>();
        this.print("Starting new RobberKnights game.");
    }

    public Board getBoard() {
        return _board;
    }

    /**
     * addPlayer method adds a new player to the game. If the maximum number of
     * players is exceeded the method returns false.
     *
     * @param name
     * @param age
     * @return
     */
    public boolean addPlayer(String name, int age) {
        Color playerColor;
        switch (_playerTurnOrder.size()) {
            case 0:
                playerColor = Color.red;
                break;
            case 1:
                playerColor = Color.blue;
                break;
            case 2:
                playerColor = Color.green;
                break;
            case 3:
                playerColor = Color.yellow;
                break;
            default:
                return false;
        }
        Player player = new Player(name, playerColor, age);
        _players.add(player);
        addPlayersinOrder(player);
        this.print("Added new Player " + name + " (" + age + ").");
        return true;
    }

    /**
     * Adds players to the game in order of decreasing age.
     *
     * @param player
     */
    private void addPlayersinOrder(Player player) {
        if (_playerTurnOrder.isEmpty()) {
            _playerTurnOrder.add(player);
        } else {
            for (int i = 0; i < _playerTurnOrder.size(); i++) {
                if (player.getAge() > _playerTurnOrder.get(i).getAge()) {
                    _playerTurnOrder.add(i, player);
                    return;
                }
            }
            _playerTurnOrder.add(player);
        }
    }

    /**
     * getPlayers return a collection of players playing the game.
     */
    @SuppressWarnings("unchecked")
    public LinkedList<Player> getPlayers() {
        return (LinkedList<Player>) _players.clone();
    }

    /**
     * returns currently playing Player.
     */
    public Player getCurrentPlayer() {
        return _playerTurnOrder.peek();
    }

    /**
     * nextTurn method corresponds to "end turn" button in GUI. The game assigns
     * control to the next player if available.
     */
    public boolean nextTurn() {
        if (_playerTurnOrder.isEmpty()) {
            return false;
        }
        //Must always play at least one tile per turn.
        if (_state == GameState.initialTilePlacement && _board.getPlayableTiles() > 0) {
            return false;
        }
        if (_state == GameState.regularPlay && _board.getPlayableTiles() >= 3) {
            return false;
        }
		Player prev = _playerTurnOrder.remove();
		if (prev.getHandSize() > 0)
			_playerTurnOrder.addLast(prev);

        if (_playerTurnOrder.size() == 0) {
            _state = GameState.end;
        }

        _board.nextTurn();

        return true;
    }

    /**
     * Returns whether or not the game has been initialized.
     */
    public boolean isGameStarted() {
        return _started;
    }

    /**
     * Once the user is done adding players, GUI class calls startGame to setup
     * the current game.
     */
    public void startGame() {
        _started = true;
        _board = new Board(this);
    }

    /**
     * Returns one of three different game states: initial, regular play, end
     */
    public GameState getState() {
        return _state;
    }

    /**
     * Sets the game state to regular play
     */
    protected void proceedToRegularPlay() {
        _state = GameState.regularPlay;
    }
    public Tile getSelectedTile() {
        return _selectedTile;
    }

    public boolean placeTile(int x, int y) {
    	if(_selectedTile == null)
    		return false;
    	
        boolean success = getCurrentPlayer().placeTile(_selectedTile, x, y, _board);
        if (success) {
            _selectedTile = null;
        }
        return success;
    }
    
    public void setSelectedTile(Tile tile) {
        _selectedTile = tile;
    }
    
    public LinkedList<Player> getWinners() {
    	if(_state == GameState.end) {
	    	int highscore = 0;
	    	//(score, knights)
	    	SimpleEntry<Integer, Integer> highKnightsForScore = new SimpleEntry<Integer, Integer>(0, 0);
	    	for(Player player : _players) {
	    		if(player.getScore() >= highscore) {
	    			highscore = player.getScore();
	    			if(highKnightsForScore.getKey() != highscore) {
	    				highKnightsForScore = new SimpleEntry<Integer, Integer>(highscore, player.getKnightsSize());
	    			} else if (player.getKnightsSize() > highKnightsForScore.getValue()){
	    				highKnightsForScore.setValue(player.getKnightsSize());
	    			}
	    		}
	    	}
	    	LinkedList<Player> winners = new LinkedList<Player>();
	    	for(Player player : _players) {
	    		if(player.getScore() >= highscore && player.getKnightsSize() >= highKnightsForScore.getValue()) {
	    			winners.add(player);
	    		}
	    	}
	    	return winners;
    	}
    	return null;
    }
}
